<template>
  <g>
    <circle :r="opt.radius" :cx="x" :cy="y" :fill="opt.bgColor" :stroke="color" :stroke-width="opt.borderWidth"/>
    <circle :r="opt.radius" :cx="x" :cy="y" fill="rgba(0, 0, 0, 0)" stroke="rgba(0, 0, 0, 0)" :stroke-width="opt.borderWidth" ref="node" @mouseover="mouseover" @mouseout="mouseout"/>
  </g>
</template>

<script>
import { TweenMax } from 'gsap'
import Draggable from 'gsap/Draggable'

export default {
  props: {
    option: {},
    category: { // 1头结点2尾结点
      default: 1
    }
  },
  data () {
    return {
      isMouseOver: false, // 是否鼠标移入
      isPress: false, // 是否按下
      point: { x: 0, y: 0 }, // 中心点
      tempPoint: { x: 0, y: 0 }, // 鼠标拖动点
      tempX: 0, // 拖动起点x
      tempY: 0 // 拖动起点y
    }
  },
  methods: {
    mouseover () {
      this.isMouseOver = true
    },
    mouseout () {
      this.isMouseOver = false
    },
    /**
     * 更新连接节点时的线的终点
     * @param    {Object} event 事件
     * @Author   Jiangzhiwei
     * @datetime 2021-10-21T10:12:37+0800
     */
    refreshTempPoint (event) {
      const box = this.stage.$el
      const stageRect = box.getBoundingClientRect()
      this.tempPoint.x = event.clientX - stageRect.left
      this.tempPoint.y = event.clientY - stageRect.top
      // console.log(this.tempPoint.x, ',', this.tempPoint.y)
    },
    /**
     * 获取拖动时移入的节点
     * @return   {Object} 移入的节点组件
     * @Author   Jiangzhiwei
     * @datetime 2021-11-26T23:49:11+0800
     */
    getNode () {
      let node = null
      this.stage.getNodeComps().some(item => {
        // 不是父级节点 且 与另一节点重合
        if (item.data.id !== this.$parent.data.id && this.node.hitTest(item.$el)) {
          // 再判断两节点是否已经连接过
          let headNodeId, tailNodeId
          if (this.category === 1) { // 如果是头节点，则在后面
            headNodeId = item.data.id
            tailNodeId = this.$parent.data.id
          } else { // 反之，则在前面
            headNodeId = this.$parent.data.id
            tailNodeId = item.data.id
          }
          const lines = this.stage.data.lines
          if (lines.some(line => line.headNodeId === headNodeId && line.tailNodeId === tailNodeId)) { // 已连接
            return false
          } else { // 未连接
            node = item
            return true
          }
        }
        return false
      })
      return node
    },
    /**
     * 新增连线 虚线
     * @Author   Jiangzhiwei
     * @datetime 2021-11-26T23:54:50+0800
     */
    addLine () {
      let data
      if (this.category === 1) { // 头节点
        data = { head: this.tempPoint, tailNodeId: this.$parent.data.id }
      } else { // 尾结点
        data = { headNodeId: this.$parent.data.id, tail: this.tempPoint }
      }
      this.lineData = this.stage.addLine(data)
    },
    /**
     * 更新连线 虚线
     * @Author   Jiangzhiwei
     * @datetime 2021-10-21T15:27:03+0800
     */
    updateLine () {
      if (this.category === 1) {
        this.lineData.head = this.tempPoint
      } else {
        this.lineData.tail = this.tempPoint
      }
    },
    /**
     * 判断是否可以连接节点
     * @param  {Object} node 节点组件
     * @return {Boolean} 是否可以连接
     * @author Jiangzhiwei
     * @time   2021-11-27T01:55:31+0800
     */
    canLink (node) {
      if (this.category === 1 && node.data.hasTail) { // 头节点 且 连接节点有尾节点
        return true
      } else if (this.category === 2 && node.data.hasHead) { // 尾节点 且 连接节点有头节点
        return true
      } else { // 其他情况不可连接
        return false
      }
    },
    /**
     * 连接节点
     * @param    {Object} node node组件
     * @param    {Boolean} isOver 是否创建完成
     * @Author   Jiangzhiwei
     * @datetime 2021-10-21T10:14:18+0800
     */
    link (node, isOver) {
      if (this.category === 1) { // 头节点
        this.lineData.head = node.tail
      } else { // 尾节点
        this.lineData.tail = node.head
      }
      if (isOver) {
        if (this.category === 1) { // 头节点
          this.lineData.headNodeId = node.data.id
        } else { // 尾节点
          this.lineData.tailNodeId = node.data.id
        }
        this.lineData.state = 1 // 虚线改为实线
        // console.log(this.lineData)
        this.stage.addHistory() // 保存历史记录
      }
    },
    init () {
      this.node = Draggable.create(this.$refs.node, {
        cursor: this.opt.cursor,
        activeCursor: this.opt.cursor,
        onPress: event => {
          event.stopPropagation()
          this.isPress = true
          this.$parent.disableDrag() // 禁止父节点拖动
          this.tempX = event.clientX
          this.tempY = event.clientY
          this.refreshTempPoint(event)
          this.addLine()
        },
        onDrag: event => {
          // console.log('onDrag')
          const node = this.getNode()
          if (node && this.canLink(node)) { // 重合 且 可以连接
            this.link(node)
          } else { // 没有重合
            this.refreshTempPoint(event) // 更新光标位置点
            this.updateLine() // 更新连线终点
          }
        },
        onMove: event => {
          // console.log('onMove')
        },
        onRelease: event => {
          // console.log('onRelease')
          event.stopPropagation()
          this.isPress = false
          this.$parent.enableDrag() // 允许父节点拖动
          const node = this.getNode()
          if (node && this.canLink(node)) { // 重合 且 可以连接
            this.link(node, true)
          } else { // 没有重合
            this.stage.removeLine(this.lineData.id)
          }
          TweenMax.to(this.$refs.node, 0, { x: 0, y: 0 })
        },
        onClick: event => {
          // console.log('onClick')
        }
      })[0]
    }
  },
  computed: {
    opt () {
      const option = {
        radius: 5, // 半径大小
        bgColor: '#999', // 背景颜色
        borderWidth: 2, // 边框大小
        borderColor: '#666', // 边框颜色
        hoverBorderColor: '#eee', // 光标移入后的边框颜色
        cursor: 'default' // 拖动时的光标样式
      }
      return Object.assign(option, this.option)
    },
    x () {
      if (this.category === 1) {
        return 0
      } else {
        return this.$parent.width
      }
    },
    y () {
      return this.$parent.opt.height / 2
    },
    color () {
      return this.isMouseOver || this.isPress ? this.opt.hoverBorderColor : this.opt.borderColor
    },
    stage () {
      return this.$parent.$parent
    }
  },
  mounted () {
    this.init()
  }
}
</script>
